﻿
using com.iot.core.annotation;
using com.iot.core.data;
using com.iot.core.data.entity;
using com.iot.core.infrastructure;
using com.iot.core.manager.id;
using com.iot.core.util;
using Microsoft.Extensions.Primitives;
using MySql.Data.MySqlClient;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;

namespace com.iot.core.manager.htcls
{
    public class HtClsManager
    {
        [MethodImpl(MethodImplOptions.Synchronized)]
        static HtClsManager Create()
        {
            //create NopEngine as engine
            return Singleton<HtClsManager>.Instance ?? (Singleton<HtClsManager>.Instance = new HtClsManager());
        }
        public static HtClsManager Current
        {
            get
            {
                if (Singleton<HtClsManager>.Instance == null)
                {
                    Create();
                }

                return Singleton<HtClsManager>.Instance;
            }
        }
        #region 扫描数据
        List<HtClsDto> lAll;


        public List<HtClsDto> GetAll()
        {
            return lAll;
        }
        public async Task StartAsnyc()
        {
            lAll = new List<HtClsDto>();

            var typeFinder = Singleton<ITypeFinder>.Instance;
            var _NopDBStartup = typeFinder
                .FindClassesOfAttribute<SchemaAttribute>()
                .Select(startup => startup)
                .ToList();
            foreach (var m in _NopDBStartup)
            {
                DealCls(m);
            }


        }
        public void DealCls(Type clazz)
        {
            var mold = lAll.Where(a => a.FullName.Equals(clazz.FullName)).FirstOrDefault();
            if (mold != null)
            {
                return;
            }


            var mnew = new HtClsDto();
            mnew.Clazz = clazz;
            mnew.Ename = clazz.Name;
            mnew.FullName = clazz.FullName;
            mnew.Name = clazz.Name;
            mnew.Memo = "";
            mnew.BaseType = clazz.BaseType?.Name;
            var apiModel = clazz.GetCustomAttribute<SchemaAttribute>();
            if (apiModel != null)
            {
                mnew.Name = apiModel.Name;
                mnew.Memo = apiModel.Description;
            }
            DealFields(clazz, mnew);
            lAll.Add(mnew);
        }
        void DealFields(Type clazz, HtClsDto mcls)
        {

            var lcolnew = new List<HtClsColDto>();
            mcls.Fields = lcolnew;
            PropertyInfo[] flst = clazz.GetProperties(BindingFlags.Instance | BindingFlags.Public);

            int sortno = 1;
            foreach (var field in flst)
            {
                var mnewcol = new HtClsColDto();
                lcolnew.Add(mnewcol);

                mnewcol.Ename = field.Name;
                mnewcol.Name = field.Name;
                mnewcol.Lx = field.PropertyType.Name;

                var apiModel = field.GetCustomAttribute<SchemaAttribute>();
                if (apiModel != null)
                {
                    mnewcol.Name = apiModel.Name;
                    mnewcol.Memo = apiModel.Description;
                }
                mnewcol.Field = field;

                var p = field;
                var f = mnewcol;
                if (p.PropertyType.IsGenericType)
                {
                    if (p.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        // If it is NULLABLE, then get the underlying type. eg if "Nullable<int>" then this will return just "int"
                        f.Lx = p.PropertyType.GetGenericArguments()[0].Name;
                        f.Nullable = true;

                    }
                    else if (typeof(List<>) == p.PropertyType.GetGenericTypeDefinition())
                    {
                        f.List = true;
                        f.Lx = p.PropertyType.GetGenericArguments()[0].FullName;
                        if (f.Lx.StartsWith("System"))
                        {
                            f.Lx = f.Name;
                        }
                    }

                }

                var mRequiredAttribute = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.RequiredAttribute>();
                if (mRequiredAttribute != null)
                {
                    mnewcol.Required = true;
                    mnewcol.Nullable = false;
                }

                if (field.PropertyType.Name.ToLower() == "string")
                {
                    var mMinLengthAttribute = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.MinLengthAttribute>();
                    if (mMinLengthAttribute != null)
                    {
                        mnewcol.Vmin = mMinLengthAttribute.Length.ToString();
                    }

                    var mMaxLengthAttribute = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.MaxLengthAttribute>();
                    if (mMaxLengthAttribute != null)
                    {
                        mnewcol.Vmax = mMaxLengthAttribute.Length.ToString();
                    }

                    var mStringLengthAttribute = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.StringLengthAttribute>();
                    if (mStringLengthAttribute != null)
                    {
                        mnewcol.Vmax = mStringLengthAttribute.MaximumLength.ToString();
                        mnewcol.Vmin = mStringLengthAttribute.MinimumLength.ToString();
                    }
                }
                else
                {
                    var mRangeAttribute = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.RangeAttribute>();
                    if (mRangeAttribute != null)
                    {
                        mnewcol.Vmin = mRangeAttribute.Minimum.ToString();
                        mnewcol.Vmax = mRangeAttribute.Maximum.ToString();
                    }

                }

                var mRegularExpression = field.GetCustomAttribute<System.ComponentModel.DataAnnotations.RegularExpressionAttribute>();
                if (mRegularExpression != null)
                {
                    mnewcol.Regex = mRegularExpression.Pattern;

                }

                f.Str = f.Lx.Equals("String");
                f.Date = f.Lx.Equals("DateTime");
                f.Num = f.Lx.Equals("Int64") || f.Lx.Equals("Int32") || f.Lx.Equals("Decimal");


            } 
        }
        #endregion 扫描数据

        #region 公用方法
        public HtClsDto FindByFullName(string name)
        {
            var m = lAll.Where(a => a.FullName == name).FirstOrDefault();
            return m;
        }
        public void DealParam(Object o)
        {
            try
            {
                DealParamF(o);
            }
            catch (Exception ex)
            {
            }
        }
        void DealParamF(Object o)  
        {
            if (o == null)
            {
                return;
            }
            if(o is BasePageQto pageQto){
                if(!string.IsNullOrWhiteSpace(pageQto.Key)){
                    var svtrim = pageQto.Key;
                     svtrim=svtrim.Replace("'","").Trim();
                    pageQto.Key = svtrim;   
                }else{
                    pageQto.Key = ""; 
                }
            }
            var ot = o.GetType();
            var mcls = FindByFullName(ot.FullName);
            if (mcls == null)
            {
                return;
            }
 
            var IsQto = o is BaseQto;
            foreach(HtClsColDto mfield in mcls.Fields )
            {
                if (mfield.Str)
                {
                    var v = mfield.Field.GetValue(o);
                    if (mfield.List)
                    {
                        if (v == null)
                        {
                            continue;
                        }
                        var l = (List<String>)v;
                        for (var i = 0; i < l.Count(); i++)
                        {
                            if (l[i] == null)
                            {
                                l[i] = "";
                                continue;
                            }
                            l[i] = l[i].Trim();
                            if (IsQto)
                            {
                                l[i] = l[i].Replace("'", "");                                 
                            }
                        }
                    }
                    else
                    {
                        if (v == null)
                        {
                            mfield.Field.SetValue(o, "");
                        }
                        else
                        {
                            var sv = v.ToString();
                            var svtrim = sv.Trim();
                            if (IsQto)
                            {
                                svtrim = svtrim.Replace("'", "");
                            }
                            if (!sv.Equals(svtrim))
                            {
                                mfield.Field.SetValue(o, svtrim);
                            }
                        }
                    }
                }
            }
        }



        public Object GetFieldV(Object o, String field)
        {
            try
            {
                return GetFieldVF(o, field);
            }
            catch (Exception ex)
            {
                return null;
            }

        }
        Object GetFieldVF(Object o, String field)  
        {
            if (o == null)
            {
                return null;
            }
            var ot = o.GetType();
            var mcls = FindByFullName(ot.FullName);
            if (mcls == null)
            {
                return null;
            }
            var mfield = mcls.Fields.Where(a => a.Ename.Equals(field, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            if (mfield == null)
            {
                return null;
            }
            
            var v = mfield.Field.GetValue(o);             
            return v;
        }

        public void SetFieldV(Object o, String field, Object v)
        {
            try
            {
                SetFieldVF(o, field, v);
            }
            catch (Exception ex)
            {

            }

        }
        void SetFieldVF(Object o, String field, Object v) 
        {
            if (o == null)
            {
                return  ;
            }
            var ot = o.GetType();
            var mcls = FindByFullName(ot.FullName);
            if (mcls == null)
            {
                return  ;
            }
            var mfield = mcls.Fields.Where(a => a.Ename.Equals(field, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            if (mfield == null)
            {
                return  ;
            } 
            mfield.Field.SetValue(o, v);
        }


        public String GetFieldStrV(Object o, String field)
        {
            try
            {
                return GetFieldStrVF(o, field);
            }
            catch (Exception ex)
            {
                return "";
            }
        }
        String GetFieldStrVF(Object o, String field)  
        {
            var v = GetFieldV(o, field);
            if (v == null)
            {
                return "";
            }
            return v.ToString();
        }



        public void DealStrNullAndTrim(Object o, PropertyInfo f)
        {
            try
            {
                DealStrNullAndTrimF(o, f);
            }
            catch (Exception ex)
            {

            }
        }
        void DealStrNullAndTrimF(Object o, PropertyInfo f)  
        {
            var v=f.GetValue(o);
            if(v==null){
                f.SetValue(o,"");
                return;
            }
            var sv = v.ToString().Trim();
            if(!v.ToString().Equals(sv)){
                f.SetValue(o, sv);
            }
        }


        public bool HasField(Object o, String field)
        {
            try
            {
                return HasFieldF(o, field);
            }
            catch (Exception ex)
            {
                return false;
            }

        }
        bool HasFieldF(Object o, String field)
        {
            if (o == null)
            {
                return false;
            }

            var ot = o.GetType();
            var mcls = FindByFullName(ot.FullName);
            if (mcls == null)
            {
                return false;
            }
            var mfield = mcls.Fields.Where(a => a.Ename.Equals(field, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
            if (mfield == null)
            {
                return false; 
            }
            return true;
        }

        #endregion 公用方法


        #region 刷新数据库

        List<HtCls> ldb = null;
        List<HtClsDto> lprj = null;
        String prjName = "";
        IRepository<HtCls> htClsMapper = null;
        IRepository<HtCodeLog> htCodeLogMapper = null;
        public void RefreshToDb(String prjName)
        {
            htClsMapper = EngineContext.Current.Resolve<IRepository<HtCls>>();
            htCodeLogMapper = EngineContext.Current.Resolve<IRepository<HtCodeLog>>();

            this.prjName = prjName;
            lprj = lAll.Where(a => a.FullName.StartsWith(prjName)).ToList();
            ldb=htClsMapper.GetAll(true).Where(a=>a.PrjName.Equals(prjName)).ToList();  
             
            foreach (var mcls in lprj)
            {
                RefreshToDbCls(mcls);
            }

            foreach(var mdb in ldb)
            {
                if (mdb.Deleted )
                {
                    continue;
                }

                var mold = lprj.Where(a=>a.FullName.Equals(mdb.FullName))
                        .FirstOrDefault();
                if (mold==null)
                {

                    htClsMapper.Delete(mdb, true);

                    var mcodelog = CreateHtCodeLogByDto(mdb);
                    mcodelog.Memo= "删除";
                    htCodeLogMapper.Insert(mcodelog, true);
                }
            }

            htClsMapper.DB.SaveChanges();
            htClsMapper = null;
            htCodeLogMapper = null;
            ldb = null;
            lprj = null;

        }

        HtCodeLog CreateHtCodeLogByDto(HtCls mdb)
        {
            var mcodelog = new HtCodeLog();
            mcodelog.Id=HtIdManager.Current.NextLong();
            mcodelog.Bid = mdb.Id;
            mcodelog.CrtDt = DateTime.Now;
            mcodelog.FullName = mdb.FullName;
            mcodelog.Lx = mdb.BaseType;
            mcodelog.Memo = "";
            mcodelog.Name = mdb.Name;
            mcodelog.PrjName = mdb.PrjName;
            return mcodelog;
        }

        void RefreshToDbCls(HtClsDto mcls)
        {
            var mdb = ldb.Where(a => a.FullName.Equals(mcls.FullName)).FirstOrDefault();
            if (mdb==null)
            {
                mdb = new HtCls();
                mdb.Id = HtIdManager.Current.NextLong();
                mdb.BaseType = mcls.BaseType;
                mdb.FullName = mcls.FullName;
                mdb.JsonFields = MyJSON.ToJSONString( mcls.Fields);
                mdb.Name = mcls.Name;
                mdb.Memo = mcls.Memo;
                mdb.PrjName = prjName;
                mdb.CrtDt = DateTime.Now; ;
                mdb.EdtDt = DateTime.Now;

                htClsMapper.Insert(mdb);
                var mcodelog = CreateHtCodeLogByDto(mdb);
                mcodelog.Memo="新增";
                htCodeLogMapper.Insert(mcodelog, true);
            }
            else
            {
                var msg = CompareCls(mcls, mdb);
                if (mdb.Deleted )
                {
                    mdb.Deleted=false;
                    msg = "删除恢复\r\n" + msg;
                }
                if (!msg.Equals(""))
                {
                    mdb.EdtDt = DateTime.Now;
                    mdb.JsonFields = MyJSON.ToJSONString(mcls.Fields);
                    htClsMapper.Update(mdb, true);

                    var mcodelog = CreateHtCodeLogByDto(mdb);
                    mcodelog.Memo = msg; 
                    htCodeLogMapper.Insert(mcodelog, true);
                }
            }
        }

        String CompareClsField(HtClsColDto mf, HtClsColDto mdb, String fieldName)
        {
            var v = GetFieldStrV(mf, fieldName);
            var dbv = GetFieldStrV(mdb, fieldName);
            if (!v.Equals(dbv))
            {
                return fieldName + " " + dbv + "=>" + v;
            }
            return "";
        }
        String CompareCls(HtClsDto mcls, HtCls mdb)
        {
            var sb = new StringBuilder();
            if (!mcls.BaseType.Equals(mdb.BaseType))
            {
                sb.Append("BaseType:")
                        .Append(mdb.BaseType)
                        .Append("=>")
                        .Append(mcls.BaseType)
                        .Append("\r\n");
                mdb.BaseType = mcls.BaseType;                 
            }
            if (!mcls.Name.Equals(mdb.Name))
            {
                sb.Append("Name:")
                        .Append(mdb.Name)
                        .Append("=>")
                        .Append(mcls.Name)
                        .Append("\r\n");
                mdb.Name = mcls.Name;
            }
            if (!mcls.Memo.Equals(mdb.Memo))
            {
                sb.Append("Memo:")
                        .Append(mdb.Memo)
                        .Append("=>")
                        .Append(mcls.Memo)
                        .Append("\r\n");
                mdb.Memo = mcls.Memo;
            }

             

            var ldb = MyJSON.Parse<List<HtClsColDto>>(mdb.JsonFields);
            foreach (var mf in mcls.Fields)
            {
                var mfdb = ldb.Where(a => a.Ename.Equals(mf.Ename)).FirstOrDefault();
                if (mfdb == null)
                {
                    sb.Append("字段:")
                       .Append(mf.Ename)
                       .Append(" 新增")
                       .Append("\r\n");
                    continue;
                }


                var sf = "";
                sf += CompareClsField(mf, mfdb, "name");
                sf += CompareClsField(mf, mfdb, "memo");
                sf += CompareClsField(mf, mfdb, "lx");
                sf += CompareClsField(mf, mfdb, "list");
                sf += CompareClsField(mf, mfdb, "nullable");
                sf += CompareClsField(mf, mfdb, "str");
                sf += CompareClsField(mf, mfdb, "date");
                sf += CompareClsField(mf, mfdb, "num");
                sf += CompareClsField(mf, mfdb, "required");
                sf += CompareClsField(mf, mfdb, "regex");
                sf += CompareClsField(mf, mfdb, "vmin");
                sf += CompareClsField(mf, mfdb, "vmax");
                if (sf != "")
                {
                    sb.Append("字段:")
                        .Append(mf.Ename)
                        .Append(sf)
                        .Append("\r\n");
                }
            }         
            return sb.ToString();
      }

        #endregion


    }
}
